iT邦幫忙

2024 iThome 鐵人賽

DAY 12
0
Modern Web

Laravel 那麼好用還需要自幹框架嗎系列 第 12

Day 12:建立資料表的 make:migration 指令

  • 分享至 

  • xImage
  •  

這幾天太忙碌了,試著撰寫路由的地方有點卡進度。

今天先來往下研究資料庫相關的內容。

要建立資料庫,在 Laravel 裡面首先要先建立 migration。

通常來說會使用指令做這件事情:

php artisan make:migration create_flights_table

這段指令是怎麼做的呢?我們來搜尋看看

首先我們找到 make:migration 實際呼叫的程式,這個在 MigrateMakeCommand 裡面

namespace Illuminate\Database\Console\Migrations

#[AsCommand(name: 'make:migration')]
class MigrateMakeCommand extends BaseCommand

我們追著繼承鏈一路往上追

use Illuminate\Console\Command;

class BaseCommand extends Command
class Command extends SymfonyCommand

該說不令人意外嗎?這個部分也是繼承了 Symfony 的套件,這邊使用的是 symfony/console 這個套件的功能,協助我們撰寫 CLI 工具。

我們繼續看看

/**
 * The console command signature.
 *
 * @var string
 */
protected $signature = 'make:migration {name : The name of the migration}
	{--create= : The table to be created}
	{--table= : The table to migrate}
	{--path= : The location where the migration file should be created}
	{--realpath : Indicate any provided migration file paths are pre-resolved absolute paths}
	{--fullpath : Output the full path of the migration (Deprecated)}';

/**
 * The console command description.
 *
 * @var string
 */
protected $description = 'Create a new migration file';

這邊定義了指令的使用方式。至於實際的運作邏輯,定義在 handle() 裡面

/**
 * Execute the console command.
 *
 * @return void
 */
public function handle()
{
	// It's possible for the developer to specify the tables to modify in this
	// schema operation. The developer may also specify if this table needs
	// to be freshly created so we can create the appropriate migrations.
	$name = Str::snake(trim($this->input->getArgument('name')));

	$table = $this->input->getOption('table');

	$create = $this->input->getOption('create') ?: false;

	// If no table was given as an option but a create option is given then we
	// will use the "create" option as the table name. This allows the devs
	// to pass a table name into this option as a short-cut for creating.
	if (! $table && is_string($create)) {
		$table = $create;

		$create = true;
	}

	// Next, we will attempt to guess the table name if this the migration has
	// "create" in the name. This will allow us to provide a convenient way
	// of creating migrations that create new tables for the application.
	if (! $table) {
		[$table, $create] = TableGuesser::guess($name);
	}

	// Now we are ready to write the migration out to disk. Once we've written
	// the migration out, we will dump-autoload for the entire framework to
	// make sure that the migrations are registered by the class loaders.
	$this->writeMigration($name, $table, $create);
}

前面的部分是用來判斷使用者輸入,以及試著從使用者輸入的 migration 名稱判斷他想建立的表名,這邊我們先不看這段,關注於 writeMigration() 的邏輯

protected function writeMigration($name, $table, $create)
{
	$file = $this->creator->create(
		$name, $this->getMigrationPath(), $table, $create
	);

	$this->components->info(sprintf('Migration [%s] created successfully.', $file));
}

create() 的實作邏輯則是

/**
 * Create a new migration at the given path.
 *
 * @param  string  $name
 * @param  string  $path
 * @param  string|null  $table
 * @param  bool  $create
 * @return string
 *
 * @throws \Exception
 */
public function create($name, $path, $table = null, $create = false)
{
	$this->ensureMigrationDoesntAlreadyExist($name, $path);

	// First we will get the stub file for the migration, which serves as a type
	// of template for the migration. Once we have those we will populate the
	// various place-holders, save the file, and run the post create event.
	$stub = $this->getStub($table, $create);

	$path = $this->getPath($name, $path);

	$this->files->ensureDirectoryExists(dirname($path));

	$this->files->put(
		$path, $this->populateStub($stub, $table)
	);

	// Next, we will fire any hooks that are supposed to fire after a migration is
	// created. Once that is done we'll be ready to return the full path to the
	// migration file so it can be used however it's needed by the developer.
	$this->firePostCreateHooks($table, $path);

	return $path;
}

這邊開始遇到一個很有意思的邏輯。

我們都知道在 Laravel 的 make 指令會協助我們建立檔案,但是很少會需要自己撰寫這樣的功能,或者去研究 Laravel 怎麼做到這件事情。

在這邊我們開始可以看到,這整段的邏輯大概是取得樣板的 getStub()、取得路徑的 getPath()、確認路徑上資料夾存在的 ensureDirectoryExists() 最後將檔案透過 populateStub() 寫入。

這邊的 getStub() 實作為

/**
 * Get the migration stub file.
 *
 * @param  string|null  $table
 * @param  bool  $create
 * @return string
 */
protected function getStub($table, $create)
{
	if (is_null($table)) {
		$stub = $this->files->exists($customPath = $this->customStubPath.'/migration.stub')
						? $customPath
						: $this->stubPath().'/migration.stub';
	} elseif ($create) {
		$stub = $this->files->exists($customPath = $this->customStubPath.'/migration.create.stub')
						? $customPath
						: $this->stubPath().'/migration.create.stub';
	} else {
		$stub = $this->files->exists($customPath = $this->customStubPath.'/migration.update.stub')
						? $customPath
						: $this->stubPath().'/migration.update.stub';
	}

	return $this->files->get($stub);
}

這邊我們可以看到,根據我們的操作是一般的 migration、建立資料表或者更改資料表,會有不同的樣板可以使用。

我們先看看一般的 migration.stub

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        //
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        //
    }
};

到這邊我們就可以概略地知道,當我們下一個 make:migration 指令時,Laravel 會利用 SymfonyCommand,加上自己之前建立好的樣板,來協助寫入檔案到專案內。


上一篇
Day 11:試用 symfony/routing
下一篇
Day 13:DatabaseServiceProvider 建立資料庫連線
系列文
Laravel 那麼好用還需要自幹框架嗎18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言